/*
    user_code.cpp.
    This file is part of the "Back to the sources" music disk.
    Copyright (C) 2002 - 2009 Alex Zolotov <nightradio@gmail.com>
*/

#include "main/user_code.h"

#include "../../sunvox_engine/sunvox_engine.h"
#include "lib_jpg/jpg.h"
#include "font.h"
#include <math.h>

const UTF8_CHAR *user_window_name = "Back To The Sources. by NightRadio 2009";
const UTF8_CHAR *user_profile_name = "config.ini";
const UTF8_CHAR *user_debug_log_file_name = "log.txt";
int user_window_xsize = 640;
int user_window_ysize = 640;
int user_window_flags = WIN_INIT_FLAG_SCALABLE;

sunvox_engine g_sv;
const UTF8_CHAR *g_tracks[] = 
{
    "data/timeless.sunvox",
    "data/solim.sunvox",
    "data/pixel_cave.sunvox",
    "data/space_trip.sunvox",
    "data/pong.sunvox",
    "data/8bit_tales.sunvox",
    "data/elochka.sunvox",
    "data/city_dreams.sunvox",
    "data/pixel_disco.sunvox",
    "data/space_seq.sunvox",
    "data/forests.sunvox",
    "data/window.sunvox",
    "data/where.sunvox",
    "data/circles.sunvox",
    "data/alchemy_masters.sunvox",
    "data/waterfalls.sunvox",
    0
};
const UTF8_CHAR *g_track_names[] = 
{
    "Timeless",
    "Solim",
    "Pixel Cave",
    "Space Trip",
    "Pong",
    "8Bit Tales",
    "Elochka",
    "City Dreams",
    "Pixel Discoteque",
    "Space Sequences",
    "Northern Forests",
    "Window",
    "Where",
    "Circles",
    "Alchemy Masters",
    "Great Waterfalls",
    0
};
int g_cur_track = 0;
int g_net_xoffset = 0;
int g_net_yoffset = 0;

struct sound_net_item
{
    int synth_id;
    int x, y;
    float v1;
    float v;
    int active;
    float code_offset;
};

volatile float g_sound_fade_val = 0;
float g_sound_fade_step = 0.001;
int g_sound_fade = 0;
float g_vol = 1;
float g_vol_transp = 0;

float g_text_deadline = 0;
int g_text_show_all = 0;

int render_piece_of_sound( sound_struct *ss )
{
    int handled = 0;
    
    if( ss && ss->user_data )
    {
	int buffer_type;
        if( ss->mode & SOUND_MODE_INT16 )
            buffer_type = 0;
        if( ss->mode & SOUND_MODE_FLOAT32 )
            buffer_type = 1;
        sunvox_render_piece_of_sound(
            buffer_type,
            ss->out_buffer,
            ss->out_frames,
            ss->channels,
            ss->freq,
            ss->out_time,
            (sunvox_engine*)ss->user_data );
        if( ( ss->mode & SOUND_MODE_FLOAT32 ) && g_sound_fade )
	{
	    float *bf = (float*)ss->out_buffer;
	    for( int i = 0; i < ss->out_frames; i++ )
	    {
		for( int c = 0; c < ss->channels; c++ )
		{
		    bf[ i * ss->channels + c ] *= g_sound_fade_val;
		}
		g_sound_fade_val -= g_sound_fade_step;
		if( g_sound_fade_val < 0 ) g_sound_fade_val = 0;
	    }
	}
        if( ( ss->mode & SOUND_MODE_FLOAT32 ) && g_vol != 1 )
	{
	    float *bf = (float*)ss->out_buffer;
	    for( int i = 0; i < ss->out_frames * ss->channels; i++ )
	    {
		bf[ i ] *= g_vol;
	    }
	}
        handled = 1;    
    }
    
    return handled;
}

int g_start_flag2 = 1;

void load_track( void )
{
    g_start_flag2 = 1;

    const UTF8_CHAR *name = g_tracks[ g_cur_track ];
    if( name && name[ 0 ] != 0 )
    {
	sunvox_load_song( name, &g_sv );
	
	g_net_xoffset = 0; g_net_yoffset = 0;

	int xmin = 999999;
	int ymin = 999999;
	int xmax = -999999;
	int ymax = -999999;
	for( int i = 0; i < g_sv.net->items_num; i++ )
	{
	    if( g_sv.net->items[ i ].flags )
	    {
		int x = g_sv.net->items[ i ].x - 512;
		int y = g_sv.net->items[ i ].y - 512;
		if( x < xmin ) xmin = x;
		if( y < ymin ) ymin = y;
		if( x > xmax ) xmax = x;
		if( y > ymax ) ymax = y;
	    }
	}
	g_net_xoffset = -( xmin + ( xmax - xmin ) / 2 );
	g_net_yoffset = -( ymin + ( ymax - ymin ) / 2 );

	//Play:
	g_sound_fade = 0;
	sunvox_play_from_beginning( &g_sv );
	g_sv.stop_at_the_end_of_song = 1;
    }
}

void next_track( void )
{
    g_cur_track++;
    if( g_tracks[ g_cur_track ] == 0 )
	g_cur_track = 0;
    g_sound_fade_val = 1;
    g_sound_fade = 1;
    while( g_sound_fade_val > 0 ) {}
    load_track();
}

void previous_track( void )
{
    g_cur_track--;
    if( g_cur_track < 0 )
    {
	for( g_cur_track = 0; ; g_cur_track++ )
	{
	    if( g_tracks[ g_cur_track ] == 0 )
	    {
		g_cur_track--;
		break;
	    }
	}
    }
    load_track();
}

int g_font_xsize = 0;
int g_font_ysize = 0;

void gl_draw_char( float x, float y, float xsize, float ysize, int no_interp, UTF8_CHAR c )
{
    glBindTexture( GL_TEXTURE_2D, 10 );
    if( no_interp )
    {
	glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
	glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
    }
    glBegin( GL_POLYGON );
    float off = ( (float)c * 8 ) / ( 128 * 8 );
    float ty = ( 1.0F / 128 ) * ( (float)g_font_ysize / 8 );
    float tx = (float)g_font_xsize / 8;
    glTexCoord2f( 0, 0 + off );		glVertex3f( x, y + ysize, 0 );
    glTexCoord2f( tx, 0 + off );	glVertex3f( x + xsize, y + ysize, 0 );
    glTexCoord2f( tx, ty + off );	glVertex3f( x + xsize, y, 0 );
    glTexCoord2f( 0, ty + off );	glVertex3f( x, y, 0 );
    glEnd();
    if( no_interp )
    {
	glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
	glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
    }
}

float gl_char_xsize( float size )
{
    return ( (float)g_font_xsize / 580 ) * size;
}

float gl_char_ysize( float size )
{
    return ( (float)g_font_ysize / 580 ) * size;
}

void gl_draw_string( float x, float y, float size, int no_interp, const UTF8_CHAR *str )
{
    float xsize = (float)g_font_xsize / 580;
    float ysize = (float)g_font_ysize / 580;
    xsize *= size;
    ysize *= size;
    float start_x = x;
    int ptr = 0;
    while( *str > 0 )
    {
	if( ptr >= (int)g_text_deadline ) break;
	UTF8_CHAR c = *str;
	if( c >= 0x61 ) c -= 0x20;
	if( c == 0x0A )
	{
	    x = start_x;
	    y -= ysize;
	    str++;
	    ptr++;
	    continue;
	}
	if( c == 0x0D )
	{
	    str++;
	    ptr++;
	    continue;
	}
	if( c != ' ' )
	    gl_draw_char( x, y, xsize, ysize, no_interp, c );
	x += xsize;
	str++;
	ptr++;
    }
}

void gl_draw_string_aa( float x, float y, float size, float r, float g, float b, float a, const UTF8_CHAR *str )
{
    float off = 0.003;
    //glColor4f( r, g, b, a );
    //gl_draw_string( x, y, size, 1, str );
    glColor4f( r, g, b, a );
    gl_draw_string( x, y, size, 0, str );
    /*gl_draw_string( x + off, y, size, 1, str );
    gl_draw_string( x - off, y, size, 1, str );
    gl_draw_string( x, y + off, size, 1, str );
    gl_draw_string( x, y - off, size, 1, str );*/
}

int my_desktop_handler( sundog_event *evt, window_manager *wm )
{
    int retval = 0;
    WINDOWPTR win = evt->win;
    
    switch( evt->type )
    {
	case EVT_AFTERCREATE:
	    retval = 1;
	    break;
	case EVT_BEFORECLOSE:
	    retval = 1;
	    break;
	case EVT_DRAW:
	    retval = 1;
	    break;
	case EVT_MOUSEBUTTONDOWN:
	case EVT_MOUSEMOVE:
	    break;
	case EVT_MOUSEBUTTONUP:
	    break;
    }
    
    return retval;
}

uchar *g_back_texture = 0;
uchar *g_wave_texture = 0;
uchar *g_glow_texture = 0;
#define CODE_TEXTURES 2
uchar *g_code_texture[ CODE_TEXTURES + 1 ];
uchar *g_font_texture = 0;

#define EXS 2
double g_ex_timer[ EXS ];
float g_ex_v[ EXS ];

void user_init( window_manager *wm )
{
    set_seed( 2 );

    //Explosions:
    for( int e = 0; e < EXS; e++ )
    {
	g_ex_timer[ e ] = 2 + ( (double)( pseudo_random() & 255 ) / 256 ) * 8;
	g_ex_v[ e ] = 0;
    }
    
    //Init textures:
    int xsize;
    int ysize;
    int mask;
    g_back_texture = load_jpeg_rgb24( "data/back.jpg" );
    xsize = jpg_xsize;
    ysize = jpg_ysize;
    int ptr = 0;
    for( int y = 0; y < ysize; y++ )
    {	
	for( int x = 0; x < xsize; x++ )
	{
	    int v = g_back_texture[ ptr * 3 ];
	    v += g_back_texture[ ptr * 3 + 1 ];
	    v += g_back_texture[ ptr * 3 + 2 ];
	    v /= 3;
	    g_back_texture[ ptr ] = (uchar)v;
	    ptr++;
	}
    }
    glEnable( GL_TEXTURE_2D );
    glBindTexture( GL_TEXTURE_2D, 2 );
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
    glTexImage2D( 
	GL_TEXTURE_2D,
        0,
        GL_ALPHA8,
        xsize, ysize,
        0,
        GL_ALPHA,
        GL_UNSIGNED_BYTE,
        g_back_texture );
	
    //Wave:
    xsize = 16;
    ysize = 16;
    g_wave_texture = (uchar*)MEM_NEW( HEAP_DYNAMIC, xsize * ysize );
    ptr = 0;
    for( int y = 0; y < ysize; y++ )
    {	
	for( int x = 0; x < xsize; x++ )
	{
	    int v;
	    if( x < 8 ) 
		v = x * 32;
	    else
		v = 255 - ( ( x * 32 ) & 255 );
	    g_wave_texture[ ptr ] = (uchar)v;
	    ptr++;
	}
    }
    glBindTexture( GL_TEXTURE_2D, 3 );
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
    glTexImage2D( 
	GL_TEXTURE_2D,
        0,
        GL_ALPHA8,
        xsize, ysize,
        0,
        GL_ALPHA,
        GL_UNSIGNED_BYTE,
        g_wave_texture );

    //Glow:
    g_glow_texture = load_jpeg_rgb24( "data/glow.jpg" );
    xsize = jpg_xsize;
    ysize = jpg_ysize;
    ptr = 0;
    for( int y = 0; y < ysize; y++ )
    {	
	for( int x = 0; x < xsize; x++ )
	{
	    g_glow_texture[ ptr ] = g_glow_texture[ ptr * 3 ];
	    ptr++;
	}
    }
    glBindTexture( GL_TEXTURE_2D, 4 );
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
    glTexImage2D( 
	GL_TEXTURE_2D,
        0,
        GL_ALPHA8,
        xsize, ysize,
        0,
        GL_ALPHA,
        GL_UNSIGNED_BYTE,
        g_glow_texture );

    //Code
    for( int n = 0; n < CODE_TEXTURES + 1; n++ )
    {
	UTF8_CHAR code_name[] = "data/code0.jpg";
	code_name[ 9 ] = '0' + n;
	g_code_texture[ n ] = load_jpeg_rgb24( (const UTF8_CHAR*)code_name );
	xsize = jpg_xsize;
	ysize = jpg_ysize;
	ptr = 0;
	for( int y = 0; y < ysize; y++ )
	{	
	    for( int x = 0; x < xsize; x++ )
	    {
		g_code_texture[ n ][ ptr ] = g_code_texture[ n ][ ptr * 3 ];
		ptr++;
	    }
	}
	glBindTexture( GL_TEXTURE_2D, 5 + n );
	glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
	glTexImage2D( 
	    GL_TEXTURE_2D,
    	    0,
    	    GL_ALPHA8,
    	    xsize, ysize,
    	    0,
    	    GL_ALPHA,
    	    GL_UNSIGNED_BYTE,
    	    g_code_texture[ n ] );
    }

    //Font:
    g_font_xsize = 6;
    g_font_ysize = 6;
    xsize = 8;
    ysize = 8 * 128;
    g_font_texture = (uchar*)MEM_NEW( HEAP_DYNAMIC, xsize * ysize );
    mem_set( g_font_texture, xsize * ysize, 0 );
    ptr = 0;
    for( int y = 0; y < ysize; y++ )
    {
        int line = font[ y ];
        for( int x = 0; x < xsize; x++ )
        {
    	    if( line & 128 )
		g_font_texture[ ptr ] = 255;
	    else
	        g_font_texture[ ptr ] = 0;
	    line <<= 1;
	    ptr++;
	}
    }
    glBindTexture( GL_TEXTURE_2D, 10 );
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
    glTexImage2D( 
	GL_TEXTURE_2D,
        0,
        GL_ALPHA8,
        xsize, ysize,
        0,
        GL_ALPHA,
        GL_UNSIGNED_BYTE,
        g_font_texture );

    //Desktop:
    wm->root_win = new_window( 
	"Desktop", 
	0, 0, 
	wm->screen_xsize, wm->screen_ysize, 
	wm->white, 
	0, 
	my_desktop_handler,
	wm );

    show_window( wm->root_win, wm );
    recalc_regions( wm );
    draw_window( wm->root_win, wm );

    //Init sound engine:
    sunvox_engine_init( 0, &g_sv );
    g_snd.user_data = (void*)&g_sv;
    //Load track:
    load_track();
}

int g_start_flag = 1;
time_t g_start_t = 0;
double g_prev_t = 0;
sound_net_item g_items[ 256 ];

int user_event_handler( sundog_event *evt, window_manager *wm )
{
    int handled = 0;

    switch( evt->type )
    {
	case EVT_BUTTONDOWN:
	    if( evt->key == KEY_UP )
	    {
		g_vol += 0.1;
		g_vol_transp = 1;
	    }
	    if( evt->key == KEY_DOWN )
	    {
		if( g_vol > 0 )
		    g_vol -= 0.1;
		g_vol_transp = 1;
	    }
	    if( evt->key == KEY_RIGHT )
	    {
		next_track();
	    }
	    if( evt->key == KEY_LEFT )
	    {
		previous_track();
	    }
	    if( evt->key == KEY_ESCAPE )
	    {
		wm->exit_request = 1;
	    }
	    handled = 1;
	    break;
	case EVT_NULL:
	{
	    time_t t_i = time_ticks();
	    
	    if( g_start_flag ) 
	    {
		g_start_t = t_i;
		g_start_flag = 0;
	    }
	    
	    t_i -= g_start_t;

	    double t = (double)t_i / (double)time_ticks_per_second();
	    double dt = t - g_prev_t;
	    g_prev_t = t;
	    
	    if( g_text_show_all == 0 )
		g_text_deadline = t * 20;
	    if( g_text_deadline > 512 )
	    {
		g_text_show_all = 1;
	    }

	    glDisable( GL_DEPTH_TEST );	    
	    glPushMatrix();
	    glLoadIdentity();

	    //Draw background:
	    glPushMatrix();	
	    float coef;
	    if( wm->screen_ysize > 0 )
		coef = (float)wm->screen_xsize / (float)wm->screen_ysize;
	    else
		coef = 0;
	    glScalef( 1, coef, 1 );
	    glClear( GL_COLOR_BUFFER_BIT );
	    int parts_mask;
	    int x, y;
	    int i;
	    int ptr;
	    for( int b = 0; b < 2; b++ )
	    {
		glPushMatrix();	
		float color_scale = 0.5F;
		if( b == 0 )
		{
		    glScalef( 1.5F, 1.5F, 1.0F );
    		    glRotatef( t * 2, 0, 0, 1 );
		    glColor4f( 0.5 * color_scale, 0.5 * color_scale, 0.5 * color_scale, 1.0F );
		}
		else
		{
		    glScalef( 1.5F, 1.5F, 1.0F );
    		    glRotatef( -t, 0, 0, 1 );
		    glColor4f( 0, 0, 0, 1 );
		}
		glEnable( GL_TEXTURE_2D );
		glBindTexture( GL_TEXTURE_2D, 2 );
		glBegin( GL_POLYGON );
		glTexCoord2f( 0, 0 );	glVertex3f( -1, 1, 0 );
		glTexCoord2f( 1, 0 );	glVertex3f( 1, 1, 0 );
		glTexCoord2f( 1, 1 );	glVertex3f( 1, -1, 0 );
		glTexCoord2f( 0, 1 );	glVertex3f( -1, -1, 0 );
		glEnd();
		glPopMatrix();	
	    }
    	    glPopMatrix();	
	    
	    if( wm->screen_xsize > 0 )
		coef = (float)wm->screen_ysize / (float)wm->screen_xsize;
	    else
		coef = 0;
	    glScalef( coef, 1.0F, 1.0F );
	    
	    //Draw sound network:
	    glEnable( GL_LINE_SMOOTH );
	    glEnable( GL_LINE_SMOOTH_HINT );
	    sunvox_engine *s = &g_sv;
	    psynth_net *net = s->net;

	    float csize = 3.2;
	    float txt_x = -1 / coef + gl_char_xsize( csize ) * 2;
	    float txt_y = -1 + gl_char_ysize( csize ) * 4;
	    gl_draw_string_aa( txt_x, txt_y, csize, 1, 1, 1, 0.3, "NightRadio:" );
	    gl_draw_string_aa( txt_x, txt_y, csize, 1, 1, 1, 1, "\nBack to the sources" );
	    gl_draw_string_aa( txt_x + gl_char_xsize( csize ) * 20, txt_y, csize, 1, 1, 1, 0.3, "!\n!\n!" );
	    UTF8_CHAR ts[ 3 ];
	    ts[ 0 ] = g_cur_track / 10 + '0';
	    ts[ 1 ] = g_cur_track % 10 + '0';
	    ts[ 2 ] = 0;
	    gl_draw_string_aa( txt_x + gl_char_xsize( csize ) * 22, txt_y - gl_char_ysize( csize ), csize, 1, 1, 1, 1, ts );
	    gl_draw_string_aa( txt_x + gl_char_xsize( csize ) * 25, txt_y - gl_char_ysize( csize ), csize, 1, 1, 1, 1, g_track_names[ g_cur_track ] );

	    //Show current position:
	    float line = (float)sunvox_frames_get_value( SUNVOX_F_CHANNEL_LINES, &g_sv );
	    float lines = (float)g_sv.song_lines;
    	    glDisable( GL_TEXTURE_2D );
	    glColor4f( 1.0F, 1.0F, 1.0F, 1.0F );
	    float sb = gl_char_ysize( csize ) / 10;
	    glBegin( GL_POLYGON );
	    glColor4f( 1.0F, 1.0F, 1.0F, 0.7F );
	    glVertex3f( txt_x, txt_y - gl_char_ysize( csize ) * 1 - sb, 0 );
	    glColor4f( 1.0F, 1.0F, 1.0F, 0.5F );
	    glVertex3f( txt_x + ( line / lines ) * gl_char_xsize( csize ) * 19, txt_y - gl_char_ysize( csize ) * 1 - sb, 0 );
	    glColor4f( 1.0F, 1.0F, 1.0F, 0.5F );
	    glVertex3f( txt_x + ( line / lines ) * gl_char_xsize( csize ) * 19, txt_y - gl_char_ysize( csize ) * 2 + sb, 0 );
	    glColor4f( 1.0F, 1.0F, 1.0F, 0.7F );
	    glVertex3f( txt_x, txt_y - gl_char_ysize( csize ) * 2 + sb, 0 );
    	    glEnd();

	    //Border:
	    glColor4f( 1.0F, 1.0F, 1.0F, 0.1F );
	    glBegin( GL_LINE_STRIP );
	    glVertex3f( -1 / coef + gl_char_xsize( csize ), 1 - gl_char_ysize( csize ), 0 );
	    glVertex3f( 1 / coef - gl_char_xsize( csize ), 1 - gl_char_ysize( csize ), 0 );
	    glVertex3f( 1 / coef - gl_char_xsize( csize ), -1 + gl_char_ysize( csize ), 0 );
	    glVertex3f( -1 / coef + gl_char_xsize( csize ), -1 + gl_char_ysize( csize ), 0 );
	    glVertex3f( -1 / coef + gl_char_xsize( csize ), 1 - gl_char_ysize( csize ), 0 );
    	    glEnd();

	    //Help:
    	    glEnable( GL_TEXTURE_2D );
	    txt_y = 1 - gl_char_ysize( csize ) * 3;
	    float help_t = 1 - t / 32;
	    if( help_t > 0 )
		gl_draw_string_aa( txt_x, txt_y, csize, 1, 1, 1, help_t, "\n\n\nControl keys:\nLEFT/RIGHT - previous/next track\nUP/DOWN - volume up/down" );
	    //Volume:
	    g_vol_transp -= dt;
	    if( g_vol_transp < 0 ) g_vol_transp = 0;
	    UTF8_CHAR ts1[ 16 ];
	    UTF8_CHAR ts2[ 16 ];
	    UTF8_CHAR ts3[ 32 ];
	    if( g_vol_transp > 0 )
	    {
		sprint( ts1, "\nVolume %d%", (int)( g_vol * 100 ) );		
		gl_draw_string_aa( txt_x, txt_y, csize, 1, 1, 1, g_vol_transp, ts1 );
	    }
	    //Time:
	    int sec = g_sv.song_len / g_sv.net->sampling_freq;
            int min = sec / 60;
            ts1[ 0 ] = min / 100 + '0';
            ts1[ 1 ] = (min/10) % 10 + '0';
            ts1[ 2 ] = min % 10 + '0';
            ts1[ 3 ] = ':';
            ts1[ 4 ] = ( sec / 10 ) % 6 + '0';
            ts1[ 5 ] = sec % 10 + '0';
            ts1[ 6 ] = 0;
	    //Current time:
	    time_t cur_time = ( time_ticks() - g_sv.start_time ) / time_ticks_per_second();
            min = cur_time / 60;
            ts2[ 0 ] = min / 100 + '0';
            ts2[ 1 ] = (min/10) % 10 + '0';
            ts2[ 2 ] = min % 10 + '0';
            ts2[ 3 ] = ':';
            ts2[ 4 ] = ( cur_time / 10 ) % 6 + '0';
            ts2[ 5 ] = cur_time % 10 + '0';
            ts2[ 6 ] = 0;
	    sprint( ts3, "%s / %s", ts2, ts1 );
	    gl_draw_string_aa( txt_x, txt_y, csize, 1, 1, 1, 1, ts3 );

	    //Get synths:
	    int items_num = net->items_num;
	    if( (int)g_text_deadline < items_num )
		items_num = (int)g_text_deadline;
	    for( i = 0; i < items_num; i++ )
	    {
		sound_net_item *item = &g_items[ i ];
		if( net->items[ i ].flags & PSYNTH_FLAG_EXISTS )
		{
		    item->active = 1;
		    item->synth_id = i;
		    item->x = net->items[ i ].x + g_net_xoffset;
		    item->y = net->items[ i ].y + g_net_yoffset;
		    item->x -= 512;
		    item->y -= 512;
		    item->y = -item->y;
		    if( g_start_flag2 ) 
		    {
			g_start_flag2 = 0;
			item->code_offset = 0;
		    }

		    STYPE v = 0;
		    int v_i = sunvox_frames_get_value( SUNVOX_F_CHANNEL_SYNTH_VOL + i, &g_sv );
		    v = (STYPE)v_i / (STYPE)32768;
		    
		    v = ( (float)net->global_volume * v ) / 256.0F;
		    v *= 2.4F;
		    if( v > 1.0F ) v = 1.0F;
		    item->v1 = v;
		    item->v = v;
		}
		else
		{
		    item->active = 0;
		}
	    }
	    //float t_scale = t / 2;
	    //if( t_scale > 1 ) t_scale = 1;
	    float scale = 1.6F; // * t_scale;
	    glScalef( scale, scale, 0 );
	    //Draw all links:
	    for( i = 0; i < items_num; i++ )
	    {
		sound_net_item *item = &g_items[ i ];
		
		if( !item->active ) continue;
		
		int synth_id = item->synth_id;
		x = item->x;
		y = item->y;

		//There are links?
    		if( net->items[ synth_id ].input_num > 0 )
		{
		    for( int l = 0; l < net->items[ synth_id ].input_num; l++ )
		    {
			if( net->items[ synth_id ].input_links[ l ] >= 0 )
			{
			    int i2 = net->items[ synth_id ].input_links[ l ];
			    if( i2 < net->items_num && ( net->items[ i2 ].flags & PSYNTH_FLAG_EXISTS ) )
			    {
				int x2 = g_items[ i2 ].x;
				int y2 = g_items[ i2 ].y;
				glBindTexture( GL_TEXTURE_2D, 3 );
				glBegin( GL_LINES );
				glColor4f( 1.0F, 1.0F, 1.0F, g_items[ i2 ].v );
				//glColor4f( 1.0F, 1.0F, 1.0F, item->v );
				float wave_off = g_items[ i2 ].code_offset; //t;
				glTexCoord2f( wave_off, wave_off );
				glVertex3f( ( (float)x / 1024 ), ( (float)y / 1024 ), 0 );
				//glColor4f( 1.0F, 1.0F, 1.0F, g_items[ i2 ].v );
				glTexCoord2f( wave_off + 10.0F, wave_off + 10.0F );
				glVertex3f( ( (float)x2 / 1024 ), ( (float)y2 / 1024 ), 0 );
				glEnd();
			    }	
			}
		    }
		}
	    }
	    glDisable( GL_LINE_SMOOTH );
	    glDisable( GL_LINE_SMOOTH_HINT );
	    //Draw boxes:
	    for( i = 0; i < items_num; i++ )
	    {
		sound_net_item *item = &g_items[ i ];

		if( !item->active ) continue;

		int synth_id = item->synth_id;
		x = item->x;
		y = item->y;
		float xsize = 0.04;
		float ysize = 0.04;
		float x1 = ( (float)x / 1024 - xsize );
		float y1 = ( (float)y / 1024 - ysize );

		//if( item->v > 0.7 ) item->v = 0.7;

	        glEnable( GL_TEXTURE_2D );
	
		if( item->v1 > 0 || i == 0 )
		{
		    if( i == 0 )
		    {
	    		/*glDisable( GL_TEXTURE_2D );
			glColor4f( 0.0F, 0.0F, 0.0F, 0.15F );
			glBegin( GL_POLYGON );
			glVertex3f( x1, y1 + ysize * 2, 0 );
			glVertex3f( x1 + xsize * 2, y1 + ysize * 2, 0 );
			glVertex3f( x1 + xsize * 2, y1, 0 );
			glVertex3f( x1, y1, 0 );
			glEnd();
	    		glEnable( GL_TEXTURE_2D );*/
			glBindTexture( GL_TEXTURE_2D, 4 );
			glColor4f( 1.0F, 1.0F, 1.0F, 0.5F + sin( t * 4 + synth_id ) / 20 );
			//glColor4f( 1.0F, 1.0F, 1.0F, 0.4F + sin( t + synth_id ) / 32 );
		    }
		    else
		    {
			glBindTexture( GL_TEXTURE_2D, 5 + ( synth_id % CODE_TEXTURES ) );
			glColor4f( 1.0F, 1.0F, 1.0F, 0.5F + sin( t * 4 + synth_id ) / 20 );
			//glColor4f( 1.0F, 1.0F, 1.0F, 0.3F + sin( t + synth_id ) / 32 );
		    }
		    glBegin( GL_POLYGON );
		    item->code_offset += item->v / 34;
		    float off1 = item->code_offset; //( t / 4 ) * (float)synth_id / 5 + (float)synth_id / 8;
		    float off2 = 0;
		    float y_vol = item->v * 1.3;
		    if( y_vol > 1 ) y_vol = 1;
		    float y_top = y1 + ysize + ysize * y_vol;
		    float y_bottom = y1 + ysize - ysize * y_vol;
		    if( i == 0 )
		    {
			y_top = y1 + ysize * 2;
			y_bottom = y1;
			off1 = 0;
			off2 = 0;
		    }
		    glTexCoord2f( 0+off1, 0-off2 );	glVertex3f( x1, y_top, 0 );
		    glTexCoord2f( 1+off1, 0-off2 );	glVertex3f( x1 + xsize * 2, y_top, 0 );
		    glTexCoord2f( 1+off1, 1-off2 );	glVertex3f( x1 + xsize * 2, y_bottom, 0 );
		    glTexCoord2f( 0+off1, 1-off2 );	glVertex3f( x1, y_bottom, 0 );
		    glEnd();
		    gl_draw_string( x1, y1 + ysize * 2 + 0.002, 1, 0, net->items[ i ].item_name );
		}
		
    		glDisable( GL_TEXTURE_2D );
		if( item->v1 > 0 )
		    glColor4f( 1.0F, 1.0F, 1.0F, 0.08F );
		else
		    glColor4f( 1.0F, 1.0F, 1.0F, 0.02F );
		if( i == 0 )
		{
		    glColor4f( 1.0F, 1.0F, 1.0F, 0.08F );
		}
		glBegin( GL_LINE_STRIP );
		glVertex3f( x1, y1, 0 );
		glVertex3f( x1 + xsize * 2, y1, 0 );
		glVertex3f( x1 + xsize * 2, y1 + ysize * 2, 0 );
		glVertex3f( x1, y1 + ysize * 2, 0 );
		glVertex3f( x1, y1, 0 );
		glEnd();
    		glEnable( GL_TEXTURE_2D );
    
		for( int g = 0; g < 3; g++ )
		{
		    glPushMatrix();
		    
		    glTranslatef( x1 + xsize, y1 + ysize, 0 );
		    
		    float ss;
		    glBindTexture( GL_TEXTURE_2D, 4 );
		    if( g == 0 )
		    {
			glRotatef( t * 8, 0, 0, 1 );
			ss = 0.28F + item->v / 10;
			glColor4f( 1, 1, 1, item->v * 0.6 );
			//glColor4f( sin( t ) + 0.5, cos( t / 2 ) + 0.5, sin( t * 2 ) + 0.5, item->v * 0.6 );
		    }
		    else if( g == 1 )
		    {
			glRotatef( -t * 8, 0, 0, 1 );
			ss = 0.1;
			glColor4f( 1.0F, 1.0F, 1.0F, item->v );
		    }
		    else if( g == 2 )
		    {
			glColor4f( 1, 1, 1, item->v );
			float sss = 0.025;
			glBegin( GL_POLYGON );
			glTexCoord2f( 0, 0 );	glVertex3f( -sss, -sss, 0 );
			glTexCoord2f( 1, 0 );	glVertex3f( sss, -sss, 0 );
			glTexCoord2f( 1, 1 );	glVertex3f( sss, sss, 0 );
			glTexCoord2f( 0, 1 );	glVertex3f( -sss, sss, 0 );
			glEnd();
			glScalef( 8.0F, 0.1F, 1.0F );
			ss = 0.1;
			glColor4f( 1.0F, 1.0F, 1.0F, item->v * 0.35 );
		    }
		    glBegin( GL_POLYGON );
		    glTexCoord2f( 0, 0 );	glVertex3f( -ss, -ss, 0 );
		    glTexCoord2f( 1, 0 );	glVertex3f( ss, -ss, 0 );
		    glTexCoord2f( 1, 1 );	glVertex3f( ss, ss, 0 );
		    glTexCoord2f( 0, 1 );	glVertex3f( -ss, ss, 0 );
		    glEnd();

		    glPopMatrix();
		}
	    }
	    
	    glPopMatrix();

	    glEnable( GL_DEPTH_TEST );

	    if( g_sv.end_of_song )
	    {
		next_track();
	    }
	    
	    handled = 1;
	    break;
	}    

	case EVT_QUIT:
	    wm->exit_request = 1;
	    handled = 1;
	    break;
    }
    
    return handled;
}

void user_close( window_manager *wm )
{
    //Deinit sound engine:
    sound_stream_stop();
    sunvox_engine_close( &g_sv );
    g_snd.user_data = 0;
    sound_stream_play();

    remove_window( wm->root_win, wm );
    
    //Remove textures:
    mem_free( g_back_texture );
    mem_free( g_wave_texture );
    mem_free( g_glow_texture );
    for( int n = 0; n < CODE_TEXTURES + 1; n++ )
    {
	mem_free( g_code_texture[ n ] );
    }
    mem_free( g_font_texture );
}
